using UnityEngine;
using Terrain;
using Math;
using Cameras;
using Plane=Math.Plane;

public class ClipRenderer : MonoBehaviour
{
    public Material[] LevelMaterials;

    public Material Material;
    public Texture2D Texture;

    public int N = 255;
    public FilterMode HeightsFilterMode = FilterMode.Point;
    public IVector2 CurrPos;

    public LevelData LevelData;
    public BlockData BlockData;
    public HeightData HeightData;

    public Mesh MM;
    public Mesh M3;
    public Mesh L0;
    public Mesh L1;
    public Mesh L2;
    public Mesh L3;
    public Mesh Ring;

    void Start()
    {
        ClipMap.Create(N, out LevelData);
        ClipMap.Create(N, LevelData, out BlockData);

        Vector3[] vertices;
        int[] indices;

        ClipMap.CreateQuad(BlockData.M, out vertices, out indices);

        MM = new Mesh();
        MM.vertices = vertices;
        MM.triangles = indices;

        ClipMap.CreateRect(BlockData.M, BlockData.N, out vertices, out indices);

        M3 = new Mesh();
        M3.vertices = vertices;
        M3.triangles = indices;

        ClipMap.CreateL0(BlockData.M, BlockData.N, out vertices, out indices);

        L0 = new Mesh();
        L0.vertices = vertices;
        L0.triangles = indices;

        ClipMap.CreateL1(BlockData.M, BlockData.N, out vertices, out indices);

        L1 = new Mesh();
        L1.vertices = vertices;
        L1.triangles = indices;

        ClipMap.CreateL2(BlockData.M, BlockData.N, out vertices, out indices);

        L2 = new Mesh();
        L2.vertices = vertices;
        L2.triangles = indices;

        ClipMap.CreateL3(BlockData.M, BlockData.N, out vertices, out indices);

        L3 = new Mesh();
        L3.vertices = vertices;
        L3.triangles = indices;

        ClipMap.CreateRing(BlockData.M, BlockData.N, out vertices, out indices);

        Ring = new Mesh();
        Ring.vertices = vertices;
        Ring.triangles = indices;

        HeightMap.Create(N, out HeightData);
        HeightMap.CreateHeights(HeightsFilterMode, HeightData.TexSize, LevelData.NumLevels, LevelData.LevelOffset, out HeightData.HeightMaps);

        LevelMaterials = new Material[LevelData.NumLevels];
        for (int i=0; i<LevelMaterials.Length; i++)
            LevelMaterials[i] = new Material(Material);
    }

    void Update()
    {
        for (int i=0; i<LevelData.NumLevels; i++)
            HeightData.HeightMaps[i].filterMode = HeightsFilterMode;

        Matrix4x4 viewProj = Frustum.GetViewProjMatrix(Camera.mainCamera);
        Plane[] frustum = Frustum.GetPlanes(viewProj);
        
        Vector3 newPos = Camera.mainCamera.transform.position;
        IVector2 moveDir = default(IVector2);

        ClipMap.MovePosition(ref CurrPos, newPos, out moveDir, LevelData, BlockData);
        ClipMap.UpdateHeightMaps(CurrPos, moveDir, LevelData, BlockData, HeightData);
        ClipMap.CullBlocks(frustum, LevelData, BlockData, HeightData);
        
        DrawBlocks();
    }

    private void DrawBlocks()
    {
        MaterialPropertyBlock properties = new MaterialPropertyBlock();
        Material material;

        //SetStreamSourceFreq(1, (D3DSTREAMSOURCE_INSTANCEDATA | (UINT)1));
        //SetStreamSourceFreq(0, (D3DSTREAMSOURCE_INDEXEDDATA | 1));

        for (int level=LevelData.MinLevel; level<LevelData.NumLevels; ++level)
        {
            material = LevelMaterials[level];

            // Morphing value
            float po = Mathf.Pow(2.0f, (float)level);
            float W = BlockData.N / 10.0f;
            float Alpha = (((BlockData.N - 1.0f) * 0.5f  - W - 1.0f));
            Vector4 v4TempViewer = new Vector4(CurrPos.x, CurrPos.y, po * Alpha, po * W);

            material.SetPass(0);
            material.SetVector("_Viewer", v4TempViewer);
            material.SetTexture("_HeightMap", HeightData.HeightMaps[level]);

            if (level == LevelData.MinLevel)
            {
                // 2x LShape ( Inner Ring )
                //SetStreamSource(1, _pInstanceBuffer, (ClipMap.levelCount * 12 + 4 + l) * 8 * sizeof(float),  8 * sizeof(float) );
                //SetIndices(_pIndexBufferLShape);
			    
                //SetStreamSource(0, _pVertexBufferLShape[1], 0,  2 * sizeof(short) );
                //DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, numVerticesLShapeIndex, 0, primitiveCountLShapeIndex );

                //SetStreamSource(0, _pVertexBufferLShape[3], 0,  2 * sizeof(short) );
                //DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, numVerticesLShapeIndex, 0, primitiveCountLShapeIndex );
                
                // 4x MxM Block ( Inner Ring )
                if (BlockData.BlocksVisibleFinest>0)
                {
                    //SetStreamSourceFreq(0, (D3DSTREAMSOURCE_INDEXEDDATA | numInstancesFinest) );
                    //SetStreamSource(0, _pVertexBufferMxM, 0,  2 * sizeof(short) );
                    //SetStreamSource(1, _pInstanceBuffer, ClipMap.levelCount * 12 * 8 * sizeof(float),  8 * sizeof(float) );
                    //SetIndices(_pIndexBufferMxM);
                    //DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, numVerticesMxMIndex, 0, primitiveCountMxMIndex );

                    //Debug.Log(string.Format("drawing finest: instances: {0}", numBlocksFinest));
                    for (int i=0; i<BlockData.BlocksVisibleFinest; ++i)
                    {
                        properties.Clear();
                        properties.AddVector("_WorldPosAndScale", BlockData.Blocks[(LevelData.NumLevels*12)+i].WorldPosAndScale);
                        properties.AddVector("_TexturePosAndScale", BlockData.Blocks[(LevelData.NumLevels*12)+i].TexturePosAndScale);
                        Vector3 pos = Camera.mainCamera.transform.position+Camera.mainCamera.transform.forward;
                        Graphics.DrawMesh(MM, pos, Quaternion.identity, material, 0, null, 0, properties);
                    }
                }
            }
            else
            {
                //bool shiftx = ((_Clipmaps[l-1].clipPos.x & (int)(po)) == 0 );
                //bool shifty = ((_Clipmaps[l-1].clipPos.y & (int)(po)) == 0 );
			    
                //// TODO : use bitwise index instead 
                //int ls = 1;						
                //if ( !shiftx && !shifty) ls = 3;	
                //else if ( shiftx && !shifty) ls = 2;
                //else if ( !shiftx && shifty) ls = 0;
			    
                //SetStreamSource(1, _pInstanceBuffer, (ClipMap.levelCount * 12 + 4 + l) * 8 * sizeof(float),  8 * sizeof(float) );
                //SetStreamSource(0, _pVertexBufferLShape[ls], 0,  2 * sizeof(short) );
                //SetIndices(_pIndexBufferLShape);
                //DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, numVerticesLShapeIndex, 0, primitiveCountLShapeIndex );
            }

            // MxM Block
            if (BlockData.BlocksVisible[level] > 0)
            {
                //SetStreamSourceFreq(0, (D3DSTREAMSOURCE_INDEXEDDATA | _Clipmaps[l].numInstances) );
                //SetStreamSource(0, _pVertexBufferMxM, 0,  2 * sizeof(short) );
                //SetStreamSource(1, _pInstanceBuffer, l * 12 * 8 * sizeof(float),  8 * sizeof(float) );
                //SetIndices(_pIndexBufferMxM);
                //DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, numVerticesMxMIndex, 0, primitiveCountMxMIndex );

                //Debug.Log(string.Format("drawing level: {0}, instances: {1}", l, ClipDatas[l].numInstances));
                for (int i=0; i<BlockData.BlocksVisible[level]; ++i)
                {
                    properties.Clear();
                    properties.AddVector("_WorldPosAndScale", BlockData.Blocks[(level*12)+i].WorldPosAndScale);
                    properties.AddVector("_TexturePosAndScale", BlockData.Blocks[(level*12)+i].TexturePosAndScale);
                    Vector3 pos = Camera.mainCamera.transform.position+Camera.mainCamera.transform.forward;
                    Graphics.DrawMesh(MM, pos, Quaternion.identity, material, 0, null, 0, properties);
                }
            }

            //SetStreamSourceFreq(0, (D3DSTREAMSOURCE_INDEXEDDATA | 1) );
            //SetStreamSource(1, _pInstanceBuffer, (ClipMap.levelCount * 12 + 4 + l) * 8 * sizeof(float),  8 * sizeof(float) );

            // Mx3 Block 
            //SetStreamSource(0, _pVertexBufferMx3, 0,  2 * sizeof(short) );
            //SetIndices(_pIndexBufferMx3);
            //DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, numVerticesMx3Index, 0, primitiveCountMx3Index );

            // Outer degenerate triangles
            //SetStreamSource(0, _pVertexBufferOuterDegenerateTriangles, 0,  2 * sizeof(short) );
            //SetIndices(_pIndexBufferOuterDegenerateTriangles);
            //DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, numVerticesOuterDegenerateTrianglesIndex, 0, primitiveCountOuterDegenerateTrianglesIndex );
        }
    }
}